﻿/*
 * XDevelopHelper
 *  @Author: XTST
 *  @Git: https://gitee.com/XTST/XUnityDevelopHelper
 *  Copyright © 2013-2018 Xiaotian Jin. All rights reserved.
 * */


using System;
using System.Collections.Generic;

namespace XDevelopHelper.FSM
{
    class FSM<TOwner> : FSMBase<TOwner>, IFSM<TOwner> where TOwner : class
    {
        #region Data

        private readonly TOwner fsmOwner;
        private readonly Dictionary<string, FSMState<TOwner>> fsmStates;
        private readonly Dictionary<string, object> fsmDatas;
        private FSMState<TOwner> currentState;

        #endregion

        #region Constructor

        public FSM(string name, TOwner owner, params FSMState<TOwner>[] states) : base(name)
        {
            if (owner == null)
            {
                throw new Exception("FSM owner is invalid. 不合法的FSM持有者");
            }

            if (states == null || states.Length < 1)
            {
                throw new Exception("FSM states is invalid. 不合法的FSM状态集合");
            }

            this.fsmOwner = owner;
            this.fsmStates = new Dictionary<string, FSMState<TOwner>>();
            this.fsmDatas = new Dictionary<string, object>();

            for (int i = 0; i < states.Length; i++)
            {
                if (states[i] == null)
                {
                    throw new Exception("FSM state is invalid. 不合法的FSM状态");
                }

                string thisName = states[i].GetType().FullName;
                if (!this.fsmStates.ContainsKey(thisName))
                {
                    this.fsmStates.Add(thisName, states[i]);
                    states[i].OnInit(this);
                }
            }

            currentState = null;
        }

        #endregion

        #region Property

        public string Name
        {
            get { return base.FSMName; }
        }

        public TOwner Owner
        {
            get { return fsmOwner; }
        }

        public override bool IsRunning
        {
            get { return currentState != null; }
        }

        public override FSMState<TOwner> CurrentFSMState
        {
            get { return currentState; }
        }

        #endregion

        #region State Manager

        public void Start<T>() where T : FSMState<TOwner>
        {
            if (IsRunning)
            {
                throw new Exception(string.Format("FSM {0} is running! FSM {0} 已经在运行了！", this.Name));
            }

            FSMState<TOwner> startState = this.GetState<T>();
            if (startState == null)
            {
                throw new Exception(string.Format("Invaild state {0} in FSM {1}! 在FSM{1}中不存在状态{0}",
                    typeof(T).FullName, this.Name));
            }

            currentState = startState;
            currentState.OnEnter(this);
        }

        public bool HasState<T>() where T : FSMState<TOwner>
        {
            return this.fsmStates.ContainsKey(typeof(T).FullName);
        }

        public T GetState<T>() where T : FSMState<TOwner>
        {
            FSMState<TOwner> state = null;
            if (fsmStates.TryGetValue(typeof(T).FullName, out state))
            {
                return (T) state;
            }

            return null;
        }

        public void ChangeState<T>() where T : FSMState<TOwner>
        {
            if (currentState == null)
            {
                throw new Exception(
                    string.Format("FSM{0} is not running or current state is invaild! FSM{0}没有运行或者当前状态不合法！", this.Name));
            }

            FSMState<TOwner> nextState = GetState<T>();
            if (nextState == null)
            {
                throw new Exception(string.Format("Invaild state {0} in FSM {1}! 在FSM{1}中不存在状态{0}",
                    typeof(T).FullName, this.Name));
            }

            currentState.OnLeave(this);
            currentState = nextState;
            currentState.OnEnter(this);
        }

        #endregion

        #region Data Manager

        public bool HasData(string name)
        {
            if (string.IsNullOrEmpty(name))
            {
                throw new Exception(string.Format("Data name is invaild in FSM{0}", this.Name));
            }

            return this.fsmDatas.ContainsKey(name);
        }

        public T GetData<T>(string name)
        {
            if (string.IsNullOrEmpty(name))
            {
                throw new Exception(string.Format("Data name is invaild in FSM{0}", this.Name));
            }

            return (T) this.fsmDatas[name];
        }

        public void SetData<T>(string name, object data)
        {
            if (string.IsNullOrEmpty(name))
            {
                throw new Exception(string.Format("Data name is invaild in FSM{0}", this.Name));
            }

            if (this.fsmDatas.ContainsKey(name))
            {
                this.fsmDatas[name] = data;
            }
            else
            {
                this.fsmDatas.Add(name, data);
            }
        }

        public void RemoveData(string name)
        {
            if (string.IsNullOrEmpty(name))
            {
                throw new Exception(string.Format("Data name is invaild in FSM{0}", this.Name));
            }

            this.fsmDatas.Remove(name);
        }

        #endregion

        #region Updater

        internal override void Update(float elapseSeconds, float realElapseSeconds)
        {
            if (currentState == null)
            {
                return;
            }

            currentState.OnUpdate(this);
        }

        #endregion
    }
}